#include "stdafx.h"

#include "SceneController.h"

#include <HSModel/Model3DStorage.h>
#include <HSModel/World.h>
#include <HSModel/Water.h>
#include <HSModel/IRenderable.h>
#include <HSModel/Ground.h>
#include <HSModel/SharedConstants.h>
#include <HSModel/Model3DStorage.h>

#include <HSModel/TransformCoords.h>

#include "Model3DRenderer.h"

#include <HSModel/Landscape.h>


SceneController::SceneController(
  Ogre::SceneManager* ip_scene_manager,
  Ogre::MeshManager* ip_mesh_manager,
  const World& i_world,
  const Model3DStorage& i_storage)
  : m_world(i_world)
  , mp_scene_manager(ip_scene_manager)
  , mp_mesh_manager(ip_mesh_manager)
  , m_storage(i_storage)
  {
  m_renderers = std::map<std::string, std::shared_ptr<Model3DRenderer>>();

  m_world.SubscribeOnChangeEvent([this](EventArguments e)
    {
    // update view
    this->RenderWorld();
    });   

  m_world.SubscribeOnLandscapeChangeEvent([this](EventArguments e)
    {
    //updating view without landscape
    this->RenderOnlyGroundInWorld();
    });

  m_world.SubscribeOnChangeObjectWithoutLandscapeEvent([this](EventArguments e)
    {
    //updating view without landscape
    this->RenderOnlyObjectsInWorld();
    });

  m_world.SubscribeOnMoveEvent([this](EventArguments e)
    {
    std::string object_name = e.GetString();
    if (m_renderers.find(object_name) != m_renderers.end())
      {
      m_renderers[object_name]->SetNodePosition(Coords::WorldToUI(m_world.GetObjectPosition(object_name)));
      m_renderers[object_name]->SetNodeOrientation(m_world.GetObjectAngleAroundY(object_name));
      }
    });

  m_world.SubscribeOnAnimateEvent([this] (EventArguments e)
    {
    std::string obj_name = e.GetString();
    IRenderable* rend_obj = m_world.GetRenderableByName(obj_name);
    m_renderers.erase(obj_name);
    m_renderers[obj_name] = std::shared_ptr<Model3DRenderer>(new Model3DRenderer(m_storage.GetModel(rend_obj->GetType()),
      mp_scene_manager,
      mp_mesh_manager,
      rend_obj->GetName(),
      rend_obj->GetType(),
      Coords::WorldToUI(rend_obj->GetPosition()),
      false,
      rend_obj->GetAngleAroundY()));
    });
  }

Ogre::SceneManager* SceneController::GetSceneManager()
  {
  return mp_scene_manager;
  }

void SceneController::RenderWorld()
  {
  // clear all scene objects
  mp_scene_manager->destroyLight("pointLight");
  m_renderers.clear();

  // creating sky
  const double skybox_distance = 10000.0;
  const std::string skybox_material_name = "Sky/SkyBox";
  mp_scene_manager->setSkyDome(true, skybox_material_name, 5, 8);

  // recreating lights
  int middle = GroundConstants::CELL_COUNT * GroundConstants::CELL_SIZE / 2;

  Ogre::Light* p_point_light = mp_scene_manager->createLight("pointLight");
  p_point_light->setType(Ogre::Light::LT_POINT);
  p_point_light->setPosition(Ogre::Vector3(middle, GroundConstants::MAX_HEIGHT + 400, GroundConstants::ORIGIN_Z));
  p_point_light->setDiffuseColour(1.0, 1.0, 1.0);
  p_point_light->setSpecularColour(1.0, 1.0, 1.0);

  auto ground_objects = m_world.GetGround()->GetObjectsToRender();
  for (auto current_model : ground_objects)
    {
    m_renderers[current_model->GetName()] = std::shared_ptr<Model3DRenderer>(new Model3DRenderer(current_model->GetModel(),
      mp_scene_manager,
      mp_mesh_manager,
      current_model->GetName(),
      current_model->GetName(),
      Coords::WorldToUI(current_model->GetPosition()),
      true));
    }

  auto objects = m_world.Renderable();
  for (auto it : objects)
    {
    m_renderers[it.first] = std::shared_ptr<Model3DRenderer>(new Model3DRenderer(m_storage.GetModel(it.second->GetType()),
      mp_scene_manager,
      mp_mesh_manager,
      it.second->GetName(),
      it.second->GetType(),
      Coords::WorldToUI(it.second->GetPosition()),
      false,
      it.second->GetAngleAroundY()));
    }
  }

void SceneController::RenderOnlyGroundInWorld()
  {
  std::map<std::string, std::shared_ptr<Model3DRenderer>> new_renderers;
  // clear only ground
  m_renderers.erase("LANDSCAPE");
  for (auto it : m_renderers)
    if (it.first.find("WATER") == std::string::npos)
      new_renderers[it.first] = it.second;

  m_renderers = new_renderers;
  new_renderers.clear();

  auto ground_objects = m_world.GetGround()->GetObjectsToRender();
  for (auto current_model : ground_objects)
    {
    m_renderers[current_model->GetName()] = std::shared_ptr<Model3DRenderer>( 
      new Model3DRenderer(current_model->GetModel(),
      mp_scene_manager,
      mp_mesh_manager,
      current_model->GetName(),
      current_model->GetName(),
      Coords::WorldToUI(current_model->GetPosition()),
      true));
    }
  }

void SceneController::RenderOnlyObjectsInWorld()
  {
  std::map<std::string, std::shared_ptr<Model3DRenderer>> new_renderers;
  for (auto it : m_renderers)
    if ("LANDSCAPE" == it.first || it.first.find("WATER") != std::string::npos)
      new_renderers[it.first] = it.second;

  m_renderers = new_renderers;
  new_renderers.clear();

  for (auto it : m_world.Renderable())
    m_renderers[it.first] = std::shared_ptr<Model3DRenderer>(new Model3DRenderer(m_storage.GetModel(it.second->GetType()),
    mp_scene_manager,
    mp_mesh_manager,
    it.second->GetName(),
    it.second->GetType(),
    Coords::WorldToUI(it.second->GetPosition()),
    false,
    it.second->GetAngleAroundY()));
  }
